package org.NooLab.glue.components;

import java.util.Vector;

import org.NooLab.glue.MessageBoard;
import org.NooLab.utilities.logging.PrintLog;




/**
 * 
 * this gets instantiated by the MessageBoardFactory
 * 
 * it may be used to observe either the board or the client (on their respective side, of course)
 * 
 * the purpose of this class is NOT to collect full message, what is monitored is
 * just the rate of incoming and outgoing messages
 * 
 * This rate is used for decisions on priority of messages, and feedback to clients e.g. about the rate of pollings
 * 
 */
public class MessageBoardMonitor implements MessageBoardTrafficMonitorIntf{

	Vector<MessageBoardMonitorEntry> monInObservations = new Vector<MessageBoardMonitorEntry>();
	Vector<MessageBoardMonitorEntry> monOutObservations = new Vector<MessageBoardMonitorEntry>();
	
	MessageBoardFactory mbf; 
	MessageBoard mb;
	
	long startingTime ;
	
	
	int maxPeriodLength = 6000 ; // 10 minutes
	int maxMessageCount = 1000 ; // whatever is reached first
	
	long changeEvents = 0;
	long lastCheckingTime = 0, lastShowTime=0 ;
	long visibleFeedbackPeriod = 10000;
	
	long timeSpread = 0;
	MonitorStatistics statsIncoming, statsOutgoing ;
	
	PrintLog out;
	
	
	// ========================================================================
	public MessageBoardMonitor( MessageBoardFactory mbf, 
								PrintLog outprn){
	
		this.mbf = mbf;
		
		statsIncoming = new MonitorStatistics();
		statsOutgoing = new MonitorStatistics();
		
		startingTime = System.currentTimeMillis() ;
		
		out = outprn;	
	}
	// ========================================================================
	
	
	public double timeSinceStart( String scale, int withinScaleOnly){
		double result = 1;
		int si=1;
		try{
			
			if (scale.length()==0){ si=1; } else{
				
				if (scale.startsWith("s")){ si=2; }
				if (scale.startsWith("m")){ si=3; }
				if (scale.startsWith("h")){ si=4; }
				if (scale.startsWith("d")){ si=5; }
			}
			
			result = System.currentTimeMillis() - startingTime ;
			
			switch (si){
				case 1: result = (double)(result/1.0);
				case 2: result = (double)(result/((double)1000));
				case 3: result = (double)(result/((double)60*1000));
				case 4: result = (double)(result/((double)60*60*1000));
				case 5: result = (double)(result/((double)24*60*60*1000));
			}
			
			if (withinScaleOnly>=1){
				// this means that not the total value of a scale is returned, but only 
				// the value within the next scale, e.g. ("s",1) returns values from [0,60]
			
				double base ;
				
				base = result - 60 * 1000 ;
				result = result -base;
				
				switch (si){
					case 1: ;
					case 2: result = (double)(result/((double)1000));
					case 3: result = (double)(result/((double)60*1000));
					case 4: result = (double)(result/((double)60*60*1000));
					case 5: result = (double)(result/((double)24*60*60*1000));
				}
			
			}
			
			
			
			
			
		}catch(Exception e){
			
		}
		
		
		return result ;
	}
	
	
	@Override
	public boolean checkMonitors() {
		boolean rB=false, hb=false;
		long now = System.currentTimeMillis() ;
		long timediff ;
		
		
		try{
			

			hb = changeEvents % 5 == 0; // better larger, 50, 100
			
			if (monInObservations.size()>=2){
				monInObservations.get(monInObservations.size()-1).timestamp = now;
				timeSpread = (monInObservations.get(monInObservations.size()-1).timestamp - monInObservations.get(0).timestamp);
				
				timeSpread = (timeSpread/1000); // we refer to rate per seconds ...
			}
			
			
			// ... a small protection against recalculation if nothing happened
			if ((hb==false) && (changeEvents>=3)) {
				if (now - lastCheckingTime > 4950){
					hb = true;
				}
			}
			
			if ((hb) && (monInObservations.size()>2) ){
				// do the recalculation of the statistics
				                    				// outx out.print(4,"Monitor: checkMonitors(), "+monInObservations.size()+" items ");
				if (statsIncoming.count>0){
					statsIncoming.averageMsgSize = statsIncoming.heapedSize/statsIncoming.count;
				}
				
				statsIncoming.timeSpread = timeSpread;
				statsIncoming.recalc() ;
				
				
				if (now - lastShowTime > visibleFeedbackPeriod){
					rB = true;
					lastShowTime = now;
				}
				// now check against global policies...
				// minimal time distance between polls issued by a client
				lastCheckingTime = now;
			}
			
			if (changeEvents>1001){
				changeEvents = 1;
			} 
		}catch(Exception e){
			// e.printStackTrace();
			out.printlnErr(3, "Error (non-critical) while monitoring the MessageBoard: "+e.getMessage());
			rB = false;
		}
		return rB;
	}

	
	public void addObservation( int inoutFlag, long sizeOfMsg ){
		
		if (sizeOfMsg>0){
			addObservation( inoutFlag, sizeOfMsg, "", -1, -1, "") ;
		}
	}
		
	public void addObservation( int inoutFlag, long sizeOfMsg, String targetrole, long id1, long id2, String descriptor ){
		
		boolean bufferWithinLimits;
		
		MessageBoardMonitorEntry entry = new MessageBoardMonitorEntry();
		
		entry.timestamp = System.currentTimeMillis();
		entry.sizeOfMsg = sizeOfMsg;
		
		// other fields are optional
		entry.role = targetrole;
		entry.id1  = id1;
		entry.id2  = id2;
		entry.description = descriptor;
		
		// 
		try{
			

			if (inoutFlag<=1){
				
				
				changeEvents++;
												out.print(5,"   Monitor: add item, event no. "+changeEvents+" ,  size="+sizeOfMsg);
												out.print(5,"                   total count: "+statsIncoming.count+" ,   total size="+statsIncoming.heapedSize);
				addItem(entry);
				
				
				if (monInObservations.size()>2){
					timeSpread = monInObservations.get(0).timestamp - monInObservations.get(monInObservations.size()-1).timestamp ;
				
				
					bufferWithinLimits = (timeSpread < maxPeriodLength);
					if (bufferWithinLimits) {
						bufferWithinLimits = monInObservations.size() < maxMessageCount;
					}
					if (bufferWithinLimits == false) {
						removeFirstItem();
					}
				}		
			}else{
				// monOutObservations.add(entry);
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		
	}
	
	
	private void addItem(MessageBoardMonitorEntry entry){
	
		monInObservations.add(entry);
		statsIncoming.heapedSize = statsIncoming.heapedSize + entry.sizeOfMsg ; 
		statsIncoming.count++;
	}
	
	
	private void removeFirstItem(){
		
		// updating our sums
		
		if (monInObservations.size()>0){
			if (statsIncoming.count>0){
				statsIncoming.count = statsIncoming.count -1 ; // monInObservations.size();
			}
			statsIncoming.heapedSize = statsIncoming.heapedSize - monInObservations.get(0).sizeOfMsg;

			monInObservations.remove(0);
		}
	}

	public MonitorStatistics getStatsIncoming() {
		return statsIncoming;
	}

	@Override
	public MonitorStatistics getStats() {
		 
		return statsIncoming;
	}
	
	
	
}
